﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Kokomo.Mirror.CliMetadata
{
	public class MemberEqualityComparer : IEqualityComparer<MemberInfo>
	{
		static MemberEqualityComparer()
		{
			Instance = new MemberEqualityComparer();
		}

		public static bool Equal(MemberInfo x, MemberInfo y)
		{
			if (object.ReferenceEquals(x, y)) return true;
			if (((object)x == null) || ((object)y == null)) return false;

			if (
				object.ReferenceEquals(x.Module, y.Module)
				&& (x.MetadataToken == y.MetadataToken)
				) return true;

			bool equals =
				(
					(
						object.ReferenceEquals(x.Module,y.Module)
						&& (x.MetadataToken == y.MetadataToken)
					) || (
						(x.Name == y.Name)
						&& (x.MemberType == y.MemberType)
						&& Equal(x.DeclaringType, y.DeclaringType)
					)
				);

			if (equals)
			{
				Type type = x as Type;
				if ((object)type != null)
				{
					equals = TypesEqual(type, y as Type);
				}
				else
				{
					FieldInfo field = x as FieldInfo;
					if ((object)field != null)
					{
						equals = FieldsEqual(field, y as FieldInfo);
					}
					else
					{
						MethodBase method = x as MethodBase;
						if ((object)method != null)
						{
							equals = MethodsEqual(method, y as MethodBase);
						}
						else
						{
							PropertyInfo property = x as PropertyInfo;
							if ((object)property != null)
							{
								equals = PropertiesEqual(property, y as PropertyInfo);
							}
							else
							{
								EventInfo eventInfo = x as EventInfo;
								if ((object)eventInfo != null)
								{
									equals = EventsEqual(eventInfo, y as EventInfo);
								}
							}
						}
					}
				}
			}

			return equals;
		}

		#region Equality
		private static bool TypesEqual(Type x, Type y)
		{
			if ((object)y == null) return false;

			bool equals =
				(x.FullName == y.FullName)
				;

			return equals;
		}
		private static bool FieldsEqual(FieldInfo x, FieldInfo y)
		{
			if ((object)y == null) return false;

			bool equals =
				(x.Name == y.Name)
				;

			return equals;
		}
		private static bool MethodsEqual(MethodBase x, MethodBase y)
		{
			if ((object)y == null) return false;

			bool equals =
				(x.Name == y.Name)
				;

			if (equals)
			{
				ParameterInfo[] xParams = x.GetParameters();
				ParameterInfo[] yParams = y.GetParameters();

				equals = (xParams.Length == yParams.Length);
				if (equals)
				{
					for (int i = 0; i < xParams.Length; i++)
					{
						equals = ParametersEqual(xParams[i], yParams[i]);
						if (!equals) break;
					}
				}
			}

			return equals;
		}
		private static bool ParametersEqual(ParameterInfo x, ParameterInfo y)
		{
			bool equals =
				(x.Name == y.Name)
				&& (x.Attributes == y.Attributes)
				&& (Equal(x.ParameterType, y.ParameterType))
				;

			return equals;
		}
		private static bool PropertiesEqual(PropertyInfo x, PropertyInfo y)
		{
			if ((object)y == null) return false;

			bool equals =
				(x.Name == y.Name)
				;

			return equals;
		}
		private static bool EventsEqual(EventInfo x, EventInfo y)
		{
			if ((object)y == null) return false;

			bool equals =
				(x.Name == y.Name)
				;

			return equals;
		}
		#endregion

		public static MemberEqualityComparer Instance { get; private set; }

		private MemberEqualityComparer()
		{
		}

		public bool Equals(MemberInfo x, MemberInfo y)
		{
			return Equal(x, y);
		}

		public int GetHashCode(MemberInfo obj)
		{
			int hash =
				obj.Name.GetHashCode()
				^ obj.MemberType.GetHashCode()
				^ obj.MetadataToken
				;
			if ((object)obj.DeclaringType != null)
				hash ^= this.GetHashCode(obj.DeclaringType);

			return hash;
		}
	}
}
